package com.hex.ds.hdtp.core.app.script.transfer.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.hex.ds.hdtp.core.app.common.enums.BasicTaskStatus;
import com.hex.ds.hdtp.core.app.common.page.PageData;
import com.hex.ds.hdtp.core.app.common.page.converter.IPageAndPageDtoConverter;
import com.hex.ds.hdtp.core.app.common.page.dto.PageDto;
import com.hex.ds.hdtp.core.app.license.activation.dto.response.ScriptConvertSurplusResponseDto;
import com.hex.ds.hdtp.core.app.license.activation.service.IFuncActivationService;
import com.hex.ds.hdtp.core.app.metadata.service.ISourceInfoService;
import com.hex.ds.hdtp.core.app.script.common.service.impl.ScriptConvertFileFilter;
import com.hex.ds.hdtp.core.app.script.transfer.converter.IScriptConvertTaskConverter;
import com.hex.ds.hdtp.core.app.script.transfer.dto.request.ScriptConvertSourceListRequestDto;
import com.hex.ds.hdtp.core.app.script.transfer.dto.request.ScriptConvertTaskRequestDto;
import com.hex.ds.hdtp.core.app.script.transfer.dto.response.ScriptConvertSourceListResponseDto;
import com.hex.ds.hdtp.core.app.script.transfer.dto.response.ScriptConvertTargetListResponseDto;
import com.hex.ds.hdtp.core.app.script.transfer.dto.response.ScriptConvertTaskResponseDto;
import com.hex.ds.hdtp.core.app.script.transfer.handler.IScriptConvertHandlerService;
import com.hex.ds.hdtp.core.app.script.transfer.handler.IScriptVerifyHandlerService;
import com.hex.ds.hdtp.core.app.script.transfer.service.IScriptConvertSourceListService;
import com.hex.ds.hdtp.core.app.script.transfer.service.IScriptConvertTargetListService;
import com.hex.ds.hdtp.core.app.script.transfer.service.IScriptConvertTaskService;
import com.hex.ds.hdtp.core.inf.common.page.Page;
import com.hex.ds.hdtp.core.inf.metadata.po.SourceInfoPo;
import com.hex.ds.hdtp.core.inf.metadata.service.impl.OracleMethodService;
import com.hex.ds.hdtp.core.inf.script.transfer.po.ScriptConvertTaskPo;
import com.hex.ds.hdtp.core.inf.script.transfer.repository.IScriptConvertTaskRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.nio.charset.Charset;
import java.util.List;

/**
 * @Package: com.hex.ds.hdtp.core.app.script.transfer.service.impl
 * @ClassName ScriptConvertTaskService
 * @Description: 脚本转换任务-业务接口实现
 * @Author: wz.li
 * @Date 2023/8/14 10:37
 * @Version v2.0.2
 **/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Lazy, @Autowired})
public class ScriptConvertTaskService implements IScriptConvertTaskService {

    private final IScriptConvertTaskRepository scriptConvertTaskRepository;

    private final IPageAndPageDtoConverter pageAndPageDtoConverter;

    private final IScriptConvertTaskConverter converter;

    private final IScriptConvertSourceListService scriptConvertSourceListService;

    private final OracleMethodService sourceMethodService;

    private final ISourceInfoService sourceInfoService;

    private final IScriptConvertHandlerService scriptConvertService;

    private final ScriptConvertFileFilter scriptConvertFileFilter;

    private final IScriptConvertTargetListService scriptConvertTargetListService;

    private final IScriptVerifyHandlerService scriptVerifyHandlerService;

    private final IFuncActivationService funcActivationService;

    /**
     * 下载文件
     */
    private static final String DOWNLOAD_FILE = "download-file";

    @Value("${script.convert.relativePath}")
    private String relativePath;

    /*
     * @Method: toScriptConvertTaskPo <br>
     * @Param: [scriptConvertTaskRequestDto] <br>
     * @Return: com.hex.ds.hdtp.core.inf.script.transfer.po.ScriptConvertTaskPo <br>
     * @Description：脚本转换任务-requestDto对象转换为PO对象<br>
     * @Author： wz.li<br>
     * @Date： 2023/2/12 10:53 <br>
     * @Version： V2.0.2<br>
     */
    private ScriptConvertTaskPo toScriptConvertTaskPo(ScriptConvertTaskRequestDto scriptConvertTaskRequestDto) {
        return converter.toScriptConvertTaskPo(scriptConvertTaskRequestDto);
    }

    /*
     * @Method: queryListByPage <br>
     * @Param: [scriptConvertTaskRequestDto, pageDto] <br>
     * @Return: com.hex.ds.hdtp.core.app.common.page.PageData <br>
     * @Description：分页查询<br>
     * @Author： wz.li<br>
     * @Date： 2023/2/12 10:53 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public PageData queryListByPage(ScriptConvertTaskRequestDto scriptConvertTaskRequestDto, PageDto pageDto) {
        final Page<ScriptConvertTaskPo> pageResult = scriptConvertTaskRepository.queryListByPage(
                toScriptConvertTaskPo(scriptConvertTaskRequestDto), pageAndPageDtoConverter.toPage(pageDto));
        List<ScriptConvertTaskResponseDto> responseDtos = converter.toScriptConvertTaskResponseDtoList(pageResult.getData());
        return new PageData(responseDtos, pageResult.getTotal());
    }

    /*
     * @Method: queryById <br>
     * @Param: [pkId] <br>
     * @Return: com.hex.ds.hdtp.core.app.script.transfer.dto.response.ScriptConvertTaskResponseDto <br>
     * @Description：根据主键查询<br>
     * @Author： wz.li<br>
     * @Date： 2023/2/15 14:43 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public ScriptConvertTaskResponseDto queryById(String pkId) {
        return converter.toScriptConvertTaskResponseDto(scriptConvertTaskRepository.queryById(pkId));
    }

    /*
     * @Method: add <br>
     * @Param: [scriptConvertTaskRequestDto, multipartFile] <br>
     * @Return: java.lang.String <br>
     * @Description：新增<br>
     * @Author： wz.li<br>
     * @Date： 2023/2/12 11:51 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String createTask(ScriptConvertTaskRequestDto scriptConvertTaskRequestDto) {
        ScriptConvertTaskPo scriptConvertTaskPo = addScriptConvertTask(scriptConvertTaskRequestDto);
        if ("FILE".equals(scriptConvertTaskRequestDto.getScriptImportType())) {
            addScriptConvertList(scriptConvertTaskPo, scriptConvertTaskRequestDto);
        } else {
            proceduresContentToFile(scriptConvertTaskRequestDto, scriptConvertTaskPo);
            addScriptConvertList(scriptConvertTaskPo);
        }
        scriptConvert(scriptConvertTaskPo.getPkId());
        return scriptConvertTaskPo.getPkId();
    }

    /*
     * @Method: addScriptConvertTask <br>
     * @Param: [scriptConvertTaskRequestDto] <br>
     * @Return: com.hex.ds.hdtp.core.inf.script.transfer.po.ScriptConvertTaskPo <br>
     * @Description：新增脚本转换任务<br>
     * @Author： wz.li<br>
     * @Date： 2023/2/12 16:42 <br>
     * @Version： V2.0.2<br>
     */
    private ScriptConvertTaskPo addScriptConvertTask(ScriptConvertTaskRequestDto scriptConvertTaskRequestDto) {
        ScriptConvertTaskPo scriptConvertTaskPo = toScriptConvertTaskPo(scriptConvertTaskRequestDto);
        scriptConvertTaskRepository.add(scriptConvertTaskPo);
        scriptConvertTaskPo.setSourceFilePath(getFileRelativePath(scriptConvertTaskPo.getPkId(), "source-file"));
        scriptConvertTaskPo.setConvertFilePath(getFileRelativePath(scriptConvertTaskPo.getPkId(), "converter-file"));
        scriptConvertTaskRepository.modifyById(scriptConvertTaskPo);
        return scriptConvertTaskRepository.queryById(scriptConvertTaskPo.getPkId());
    }

    /*
     * @Method: addScriptConvertList <br>
     * @Param: [scriptConvertTaskPo, scriptConvertTaskRequestDto] <br>
     * @Return: void <br>
     * @Description：新增脚本转换清单<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/11 18:40 <br>
     * @Version： V2.0.2<br>
     */
    private void addScriptConvertList(ScriptConvertTaskPo scriptConvertTaskPo, ScriptConvertTaskRequestDto scriptConvertTaskRequestDto) {
        File sourceFile = new File(scriptConvertTaskPo.getSourceFilePath());
        for (String filePath : scriptConvertTaskRequestDto.getFileList()) {
            File uploadFile = new File(filePath);
            FileUtil.copyFilesFromDir(uploadFile, sourceFile, true);
            ScriptConvertSourceListRequestDto scriptConvertList = converter.toScriptConvertListRequestDto(scriptConvertTaskPo, uploadFile);
            scriptConvertList.setSourceFileSize(uploadFile.length() + "");
            scriptConvertSourceListService.add(scriptConvertList);
        }
    }

    /*
     * @Method: proceduresContentToFile <br>
     * @Param: [scriptConvertTaskRequestDto, scriptConvertTaskPo] <br>
     * @Return: void <br>
     * @Description：存储过程落地成文件<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/14 10:43 <br>
     * @Version： V2.0.2<br>
     */
    private void proceduresContentToFile(ScriptConvertTaskRequestDto scriptConvertTaskRequestDto, ScriptConvertTaskPo scriptConvertTaskPo) {
        SourceInfoPo sourceInfoPo = sourceInfoService.queryBySourceCode(scriptConvertTaskPo.getSourceConnCode());
        for (String procedure : scriptConvertTaskRequestDto.getProceduresList()) {
            String proceduresContent = sourceMethodService.proceduresContent(sourceInfoPo.getSourceCode(), scriptConvertTaskRequestDto.getDbName(), procedure);
            String filePath = scriptConvertTaskPo.getSourceFilePath() + File.separator + procedure + ".sql";
            FileUtil.writeString(proceduresContent, filePath, "GBK");
        }
    }

    /*
     * @Method: addScriptConvertList <br>
     * @Param: [scriptConvertTaskPo, sourceFile] <br>
     * @Return: Integer <br>
     * @Description：保存脚本转换清单<br>
     * @Author： wz.li<br>
     * @Date： 2023/2/12 18:07 <br>
     * @Version： V2.0.2<br>
     */
    private void addScriptConvertList(ScriptConvertTaskPo scriptConvertTaskPo) {
        for (File file : FileUtil.loopFiles(scriptConvertTaskPo.getSourceFilePath(), scriptConvertFileFilter)) {
            ScriptConvertSourceListRequestDto scriptConvertList = converter.toScriptConvertListRequestDto(scriptConvertTaskPo, file);
            scriptConvertList.setSourceFileSize(file.length() + "");
            scriptConvertSourceListService.add(scriptConvertList);
        }
    }

    /*
     * @Method: scriptConvert <br>
     * @Param: [pkId] <br>
     * @Return: void <br>
     * @Description：脚本转换启动<br>
     * @Author： wz.li<br>
     * @Date： 2023/2/13 14:41 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public void scriptConvert(String pkId) {
        ScriptConvertTaskResponseDto scriptConvertTask = queryById(pkId);
        if (BasicTaskStatus.TODO.name().equals(scriptConvertTask.getStatus())) {
            scriptConvertTaskRepository.modifyTaskStart(pkId, BasicTaskStatus.PROCESSING.name());
            toScriptConvert(pkId);
        }
    }


    /**
     * @Method scriptConvertType
     * @Param
     * @Return java.util.List<java.lang.String>
     * @Description 获取脚本转换插件类型
     * @Author gj.xu
     * @Date 2023/11/29 9:27
     * @Version V2.0
     */
    @Override
    public List<String> scriptConvertType() {
        return scriptConvertService.scriptConvertType();
    }

    /*
     * @Method: scriptVerify <br>
     * @Param: [pkId] <br>
     * @Return: void <br>
     * @Description：脚本验证<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/14 20:32 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public void scriptVerify(String pkId) {
        List<ScriptConvertTargetListResponseDto> scriptConvertTargetList = scriptConvertTargetListService.queryListByTaskId(pkId);
        modifyTaskToVerifyingById(pkId, scriptConvertTargetList.size());
        for (ScriptConvertTargetListResponseDto scriptConvertTargetListResponseDto : scriptConvertTargetList) {
            scriptVerifyHandlerService.scriptVerifyHandler(scriptConvertTargetListResponseDto);
        }
    }

    /*
     * @Method: modifyTaskToVerifyingById <br>
     * @Param: [pkId] <br>
     * @Return: void <br>
     * @Description：根据主键更新转换任务的验证状态<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/16 19:16 <br>
     * @Version： V2.0.2<br>
     */
    public void modifyTaskToVerifyingById(String pkId, Integer verifyTotal) {
        modifyById(new ScriptConvertTaskRequestDto().setPkId(pkId).setVerifyTotalNum(verifyTotal).setVerifyStatus(BasicTaskStatus.PROCESSING.name()));
    }

    /*
     * @Method: toScriptConvert <br>
     * @Param: [pkId] <br>
     * @Return: void <br>
     * @Description：启动脚本转换任务<br>
     * @Author： wz.li<br>
     * @Date： 2023/2/13 15:59 <br>
     * @Version： V2.0.2<br>
     */
    public void toScriptConvert(String convertTaskPkId) {
        ScriptConvertTaskPo scriptConvertTask = scriptConvertTaskRepository.queryById(convertTaskPkId);
        List<ScriptConvertSourceListResponseDto> scriptConvertList = scriptConvertSourceListService.queryByTaskId(convertTaskPkId);
        for (ScriptConvertSourceListResponseDto scriptConvertListRequestDto : scriptConvertList) {
            scriptConvertService.scriptConvertHandler(scriptConvertListRequestDto, scriptConvertTask);
        }
    }

    /*
     * @Method: modifyById <br>
     * @Param: [scriptConvertTaskRequestDto] <br>
     * @Return: void <br>
     * @Description：根据脚本转换任务主键更新<br>
     * @Author： wz.li<br>
     * @Date： 2023/2/12 10:54 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public void modifyById(ScriptConvertTaskRequestDto scriptConvertTaskRequestDto) {
        int count = scriptConvertTaskRepository.modifyById(toScriptConvertTaskPo(scriptConvertTaskRequestDto));
        if (count != 1) {
            throw new RuntimeException("更新指定转换任务失败");
        }
    }

    /**
     * @param pkId
     * @return int
     * @Method modifySuccessNum
     * @Description 更新成功数量
     * @Author jian.qiao
     * @Date 2023/2/14 15:07
     * @Version V1.0
     */
    @Override
    @Transactional
    public void modifySuccessNum(String pkId) {
        scriptConvertTaskRepository.modifySuccessNum(pkId);
        scriptConvertTaskRepository.modifyStatus(pkId);
    }

    /**
     * @param pkId
     * @return int
     * @Method modifySuccessNum
     * @Description 更新失败数量
     * @Author jian.qiao
     * @Date 2023/2/14 15:07
     * @Version V1.0
     */
    @Override
    @Transactional
    public void modifyFailNum(String pkId) {
        scriptConvertTaskRepository.modifyFailNum(pkId);
        scriptConvertTaskRepository.modifyStatus(pkId);
    }

    /*
     * @Method: batchRemoveById <br>
     * @Param: [pkIds] <br>
     * @Return: void <br>
     * @Description：根据主键批量删除<br>
     * @Author： wz.li<br>
     * @Date： 2023/2/12 10:56 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void batchRemoveById(List<String> pkIds) {
        for (String pkId : pkIds) {
            removeById(pkId);
            scriptConvertSourceListService.removeByTaskId(pkId);
            scriptConvertTargetListService.removeByTaskId(pkId);
        }
    }

    /*
     * @Method: removeById <br>
     * @Param: [pkId] <br>
     * @Return: void <br>
     * @Description：根据主键删除对应的转换任务<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/16 13:42 <br>
     * @Version： V2.0.2<br>
     */
    private void removeById(String pkId) {
        if (scriptConvertTaskRepository.removeById(pkId) != 1) {
            throw new RuntimeException("删除转换任务失败");
        }
    }

    /*
     * @Method: upload <br>
     * @Param: [multipartFileList] <br>
     * @Return: java.util.List<java.io.File> <br>
     * @Description：上传转换脚本<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/11 15:04 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public List<File> upload(List<MultipartFile> multipartFileList) {
        if (!funcActivationService.checkScriptConvertStatus()) {
            throw new RuntimeException("请联系销售获取license激活该功能");
        }
        String uploadFilePath = getTmpFilePath("upload-file");
        String sourceFilePath = getTmpFilePath("source-file");
        try {
            for (MultipartFile multipartFile : multipartFileList) {
                File uploadFile = new File(uploadFilePath + File.separator + multipartFile.getOriginalFilename());
                FileUtil.mkdir(uploadFile);
                multipartFile.transferTo(uploadFile);
                if (FileUtil.getType(uploadFile).equalsIgnoreCase("zip")) {
                    ZipUtil.unzip(uploadFile, new File(sourceFilePath), Charset.forName("GBK"));
                } else {
                    FileUtil.copyFilesFromDir(uploadFile, new File(sourceFilePath + File.separator + multipartFile.getOriginalFilename()), true);
                }
            }
        } catch (Exception e) {
            log.error("文件上传失败:{}", e.getMessage(), e);
            throw new RuntimeException("文件上传失败！");
        }
        return FileUtil.loopFiles(sourceFilePath, scriptConvertFileFilter);
    }

    /*
     * @Method: downloadFile <br>
     * @Param: [request, response, pkId] <br>
     * @Return: void <br>
     * @Description：下载源文件和转换后文件<br>
     * @Author： wz.li<br>
     * @Date： 2023/2/13 19:09 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public String downloadFile(String pkId) {
        ScriptConvertTaskPo scriptConvertTaskPo = scriptConvertTaskRepository.queryById(pkId);
        // 获取下载文件路径、源文件路径、转换文件路径
        String downloadFileRelativePath = getTmpFilePath(DOWNLOAD_FILE);
        String sourceFilSavePath = downloadFileRelativePath + File.separator + "sourceFile";
        String convertFilSavePath = downloadFileRelativePath + File.separator + "convertFile";

        // 将源文件和转换文件复制到下载目录下
        FileUtil.copyContent(new File(scriptConvertTaskPo.getSourceFilePath()), new File(sourceFilSavePath), true);
        FileUtil.copyContent(new File(scriptConvertTaskPo.getConvertFilePath()), new File(convertFilSavePath), true);

        //zip压缩下载文件
        ZipUtil.zip(downloadFileRelativePath);
        return downloadFileRelativePath + ".zip";
    }

    /**
     * @Method downloadSourceFile
     * @Param [pkId]
     * @Return java.lang.String
     * @Description 下载源文件
     * @Author Guo jiajun
     * @Date 2023/4/25 16:17
     * @Version V1.0
     */
    @Override
    public String downloadSourceFile(String pkId) {
        ScriptConvertTaskPo scriptConvertTaskPo = scriptConvertTaskRepository.queryById(pkId);
        // 获取下载文件路径、源文件路径、转换文件路径
        String downloadFileRelativePath = getTmpFilePath(DOWNLOAD_FILE);
        String sourceFilSavePath = downloadFileRelativePath + File.separator + "sourceFile";
        // 将源文件和转换文件复制到下载目录下
        FileUtil.copyContent(new File(scriptConvertTaskPo.getSourceFilePath()), new File(sourceFilSavePath), true);
        //zip压缩下载文件
        ZipUtil.zip(downloadFileRelativePath);
        return downloadFileRelativePath + ".zip";
    }

    /**
     * @Method downloadConvertFile
     * @Param [pkId]
     * @Return java.lang.String
     * @Description 下载转换后文件
     * @Author Guo jiajun
     * @Date 2023/4/25 16:17
     * @Version V1.0
     */
    @Override
    public String downloadConvertFile(String pkId) {
        ScriptConvertTaskPo scriptConvertTaskPo = scriptConvertTaskRepository.queryById(pkId);
        // 获取下载文件路径、源文件路径、转换文件路径
        String downloadFileRelativePath = getTmpFilePath(DOWNLOAD_FILE);
        String convertFilSavePath = downloadFileRelativePath + File.separator + "convertFile";
        // 将源文件和转换文件复制到下载目录下
        FileUtil.copyContent(new File(scriptConvertTaskPo.getConvertFilePath()), new File(convertFilSavePath), true);
        //zip压缩下载文件
        ZipUtil.zip(downloadFileRelativePath);
        return downloadFileRelativePath + ".zip";
    }

    /*
     * @Method: getTmpFilePath <br>
     * @Param: [fileType] <br>
     * @Return: java.lang.String <br>
     * @Description：获取临时路径<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/14 10:46 <br>
     * @Version： V2.0.2<br>
     */
    private String getTmpFilePath(String fileType) {
        String filePath = StrUtil.format("{}/tmp/{}/{}/{}", relativePath, DateUtil.today(), fileType, System.currentTimeMillis());
        FileUtil.mkdir(filePath);
        return filePath;
    }

    /*
     * @Method: getFileRelativePath <br>
     * @Param: [taskId, fileType] <br>
     * @Return: java.lang.String <br>
     * @Description：获取脚本源文件和转换文件路径<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/11 18:33 <br>
     * @Version： V2.0.2<br>
     */
    private String getFileRelativePath(String taskId, String fileType) {
        String filePath = StrUtil.format("{}/{}/{}/{}", relativePath, DateUtil.today(), taskId, fileType);
        FileUtil.mkdir(filePath);
        return filePath;
    }

    /**
     * @Method getScriptConvertSurplus
     * @Param
     * @Return com.hex.ds.hdtp.core.app.license.activation.dto.response.ScriptConvertSurplusResponseDto
     * @Description 获取脚本转换剩余量
     * @Author gj.xu
     * @Date 2024/3/26 20:07
     * @Version V2.0
     */
    public ScriptConvertSurplusResponseDto getScriptConvertSurplus() {
        return funcActivationService.getScriptConvertSurplus();
    }

}

